﻿using Bentley.CifNET.GeometryModel;
using Bentley.CifNET.GeometryModel.DgnAccess;
using Bentley.CifNET.GeometryModel.RoadwayDesigner;
using Bentley.CifNET.LinearGeometry;
using Bentley.CifNET.Model;
using Bentley.CifNET.Objects;
using CheccCoverplateCulvertAssemble.run;
using CheccCulvertHelpers;

namespace Checc.Culvert.LayingLine;

public class ProfileCalculate
{
    #region Fields

    public List<Corridor> _allRefCorridors;
    private readonly double _angleStep;
    private readonly LinearEntity2dInPlan _culvertLayoutLine;
    private readonly double _culvetSlop;
    private readonly List<LinearEntity2dInPlan> _minorAligmentList;
    private readonly IObjectSpace _objectSpace;
    private readonly LinearEntity2dInPlan _primaryAlignment;
    private readonly double _range;
    private LinearPoint _primaryIntersectPointOnCulvertLayLine;

    #endregion Fields

    #region Properties

    public DgnModel ActiveThreeDModel => this.GetActiveThreeDModelReference();

    public List<Corridor> AllRefCorridors
    {
        get
        {
            if (!this._allRefCorridors.Any())
            {
                this._allRefCorridors = this.GetAllRefCorridors();
            }
            return this._allRefCorridors;
        }
    }

    public GeometricModel GeometricModel => GeometricModel.GetGeometricModel(this._objectSpace, GetModelOptions.AllowDoNotExist);

    public LinearPoint PrimaryIntersectPointOnCulvertLayLine => this._primaryIntersectPointOnCulvertLayLine ??= this.GetPrimaryIntersectPoint();

    private unsafe DgnModel GetActiveThreeDModelReference()
    {
        DgnModelRef modelRef = DgnModelRef.GetModelRef(new IntPtr((void*)MstnPresentationEnabler.Instance.GetActiveThreeDModel()));
        return null != modelRef ? modelRef.GetDgnModel() : null;
    }

    //public Corridor PrimaryCorridor => this._primaryCorridor ??= this.GetPrimaryCorridor();

    #endregion Properties

    public ProfileCalculate()
    {
    }

    public ProfileCalculate(IObjectSpace objectSpace, LinearEntity2dInPlan primaryAlignment, List<LinearEntity2dInPlan> minorAligmentList, LinearEntity2dInPlan culvertLayoutLine, double angleStep, double culvetSlop)
    {
        this._objectSpace = objectSpace;
        this._primaryAlignment = primaryAlignment;
        this._minorAligmentList = minorAligmentList;
        this._culvertLayoutLine = culvertLayoutLine;
        this._angleStep = angleStep;
        this._culvetSlop = culvetSlop;
        this._allRefCorridors = new List<Corridor>();
        this._range = 100.0;
    }


    private List<(DPoint3d pointA, DPoint3d pointB, double distance,bool isWF)> GetAllIntersectPoints_H()
    {
        var ret = new List<(DPoint3d pointA, DPoint3d pointB ,double distance,bool isWF)>();
        foreach (var corridor in this.AllRefCorridors)
        {
            //获取三维线
            var linearEntities = corridor.GetFinalLinearEntities();
            var linesEntity = linearEntities.Where(s => s.Name.Contains("PJX-") || s.Name==("LJBY-L-1") ||  s.Name == ("LJBY-R-1"));

            foreach (var lineEntity in linesEntity)
            {
                bool isWF = lineEntity.Name.StartsWith("LJBY-") == true ? true :false;
                var pjPt = GetIntersectXYPoint(lineEntity.BaseGeometry.GetCurveVector(), this._culvertLayoutLine.ActiveLinearEntity3dValidBaseGeometry.GetCurveVector());
                double distance = pjPt.pointB.Distance(this._culvertLayoutLine.ActiveLinearEntity3d.BaseGeometry.StartPoint.Coordinates);
                ret.Add((pjPt.pointA,pjPt.pointB,distance, isWF));
            }
        }
        return ret;
    }
    /// <summary>
    /// bim中心调用
    /// </summary>
    /// <returns></returns>
    private List<(DPoint3d pointA, DPoint3d pointB, double distance, bool isWF)> GetAllIntersectPoints_H_BIM()
    {
        var ret = new List<(DPoint3d pointA, DPoint3d pointB, double distance, bool isWF)>();
        foreach (var corridor in this.AllRefCorridors)
        {
            //获取三维线
            var linearEntities = corridor.GetFinalLinearEntities();
            //var linesEntity = linearEntities.Where(s => s.Name.Contains("PJX-") || s.Name==("LJBY-L-1") ||  s.Name == ("LJBY-R-1"));
            var linesEntity = linearEntities.Where(s => s.Name == "TF1-L" || s.Name == "TF1-R" || s.Name == "LJBY-L" || s.Name == "LJBY-R" || s.Name.Contains("TLJ-EP-"));
            foreach (var lineEntity in linesEntity)
            {
                bool isWF = false;
                if (lineEntity.Name == "LJBY-L" || lineEntity.Name == "LJBY-R" || lineEntity.Name.Contains("TLJ-EP-"))
                {
                    isWF = true;
                }
                var pjPt = GetIntersectXYPoint(lineEntity.BaseGeometry.GetCurveVector(), this._culvertLayoutLine.ActiveLinearEntity3dValidBaseGeometry.GetCurveVector());
                double distance = pjPt.pointB.Distance(this._culvertLayoutLine.ActiveLinearEntity3d.BaseGeometry.StartPoint.Coordinates);
                ret.Add((pjPt.pointA, pjPt.pointB, distance, isWF));
            }
        }
        return ret;
    }
    private (DPoint3d pointA,DPoint3d pointB)  GetIntersectXYPoint(CurveVector outCurveVectorA, CurveVector curveVectorB)
    {
        outCurveVectorA.GetStartEnd(out var startPointA, out var endPointA);
        curveVectorB.GetStartEnd(out var startPointB, out var endPointB);
        CurveVector intersectionA = new CurveVector(CurveVector.BoundaryType.Inner);
        CurveVector intersectionB = new CurveVector(CurveVector.BoundaryType.Inner);
        CurveCurve.IntersectionsXY(intersectionA, intersectionB, outCurveVectorA, curveVectorB);//DMatrix4d.From(dTransform3));
        intersectionA.GetStartPoint (out DPoint3d pointA);
        intersectionB.GetStartPoint(out DPoint3d pointB);
        return (pointA,pointB);
    }

    private List<(LinearPoint intersectPoint, LinearEntity2dInPlan alignment)> GetAllIntersectPointsOfAllAlignment()
    {
        var ret = new List<(LinearPoint intersectPoint, LinearEntity2dInPlan alignment)>();
        ret.Add((this._culvertLayoutLine.BaseGeometry.Intersect(this._primaryAlignment.BaseGeometry).Item(0).PointOn1, this._primaryAlignment));
        foreach (var item in this._minorAligmentList)
        {
            ret.Add((this._culvertLayoutLine.BaseGeometry.Intersect(item.BaseGeometry).Item(0).PointOn1, item));
        }
        ret.OrderBy(p => p.intersectPoint.DistanceAlong).ToList();
        return ret;
    }

    //private List<Corridor> GetAllRefCorridors()
    //{
    //    var ret = new List<Corridor>();
    //    // 筛选主线设计廊道
    //    var point = this._primaryAlignment.BaseGeometry.Intersect(this._culvertLayoutLine.BaseGeometry);
    //    var primaryStation = this._primaryAlignment.BaseGeometry.ProjectPointOnPerpendicular(point.Item(0).PointOn1.Coordinates, 0.001).DistanceAlong;
    //    var primaryCorridors = CivilHelper.GetCorridorListByAlignmentAndDisAlongRange(this._primaryAlignment, primaryStation, this._range, this.GeometricModel);
    //    ret.AddRange(primaryCorridors);
    //    foreach (var item in this._minorAligmentList)
    //    {
    //        var minorpoint = item.BaseGeometry.Intersect(this._culvertLayoutLine.BaseGeometry);
    //        var minorStation = item.BaseGeometry.ProjectPointOnPerpendicular(minorpoint.Item(0).PointOn1.Coordinates, 0.001).DistanceAlong;
    //        var minorCorridors = CivilHelper.GetCorridorListByAlignmentAndDisAlongRange(item, minorStation, this._range, this.GeometricModel);
    //        ret.AddRange(minorCorridors);
    //    }
    //    return ret;
    //}
    private List<Corridor> GetAllRefCorridors()
    {
        var ret = new List<Corridor>();
        // 筛选主线设计廊道
        var primaryCorridors = this.GeometricModel.CorridorSet.Corridors.ToList().Where(cor => cor.BaseRule.TrueBaseHorizontal.Name == this._primaryAlignment.Name).ToList();
        ret.AddRange(primaryCorridors);
        foreach (var al in this._minorAligmentList)
        {
            foreach (var cor in this.GeometricModel.CorridorSet.Corridors)
            {
                if (cor.BaseRule.TrueBaseHorizontal.Name == al.Name)
                {
                    ret.Add(cor);
                }
            }
        }
        return ret;
    }

    public (double slope,double elevation) CalculateResultData(double culvertH)
    {
        //涵洞纵断自动计算：
        //半填半挖（填底挖高）坡度3 %
        //挖方 两侧路基边缘点Z值分别向下平移0.5，两点连线坡度不做处理
        var intersectPts = new List<(DPoint3d pointA, DPoint3d pointB)>();
        var ret = Commands.isBimCenter ? this.GetAllIntersectPoints_H_BIM() : this.GetAllIntersectPoints_H();
        var points = ret.Where(p => p.distance != 0)
            .OrderBy(p => p.distance);
        DPoint3d startPoint = new DPoint3d();
        DPoint3d endPoint = new DPoint3d();
        double slope = 0;
        double elevation = 0;
        //处理三种情况：1.填方 2.挖方 3.半填半挖
        if (points.FirstOrDefault().isWF == false && points.LastOrDefault().isWF == false)
        {
            startPoint = points.FirstOrDefault().pointA;
            endPoint = points.LastOrDefault().pointA;
            Line line = new Line(startPoint, endPoint);
            var intersectPt = GetIntersectXYPoint(line.GetCurveVector(), this._primaryAlignment.BaseGeometry.GetCurveVector());
            double leftLength = intersectPt.pointA.Distance(startPoint);
            double rightLength = intersectPt.pointA.Distance(endPoint);
            //高度差/长度 = 坡度
            slope = (intersectPt.pointA.Z - startPoint.Z) / leftLength;
            elevation = intersectPt.pointA.Z;
        }
        else if ((points.FirstOrDefault().isWF == false && points.LastOrDefault().isWF == true))
        {
            startPoint = points.FirstOrDefault().pointA;
            endPoint = new DPoint3d(points.LastOrDefault().pointA.X, points.LastOrDefault().pointA.Y, startPoint.Z);
            double zz = startPoint.Distance(endPoint) * 0.03;
            endPoint.Z = endPoint.Z + zz;
            Line line = new Line(startPoint, endPoint);
            var intersectPt = GetIntersectXYPoint(line.GetCurveVector(), this._primaryAlignment.BaseGeometry.GetCurveVector());
            slope = 0.03;
            elevation = intersectPt.pointA.Z;
        }
        else if (points.FirstOrDefault().isWF == true && points.LastOrDefault().isWF == false)
        {
            endPoint = points.LastOrDefault().pointA;
            startPoint = new DPoint3d(points.FirstOrDefault().pointA.X, points.FirstOrDefault().pointA.Y, endPoint.Z);
            double zz = startPoint.Distance(endPoint) * 0.03;
            startPoint.Z = startPoint.Z - zz;
            Line line = new Line(startPoint, endPoint);
            var intersectPt = GetIntersectXYPoint(line.GetCurveVector(), this._primaryAlignment.BaseGeometry.GetCurveVector());
            slope = -0.03;
            elevation = intersectPt.pointA.Z;
        }
        else
        {
            startPoint = points.FirstOrDefault().pointA;
            endPoint = points.LastOrDefault().pointA;
            startPoint.Z = startPoint.Z - 0.5 - culvertH;
            endPoint.Z = endPoint.Z - 0.5 - culvertH;
            Line line = new Line(startPoint, endPoint);
            var intersectPt = GetIntersectXYPoint(line.GetCurveVector(), this._primaryAlignment.BaseGeometry.GetCurveVector());
            slope = (endPoint.Z - startPoint.Z) / (startPoint.Distance(endPoint));
            elevation = intersectPt.pointA.Z;
        }
        return (slope, elevation);
    }

    private LinearPoint GetPrimaryIntersectPoint()
    {
        var ret = this._primaryAlignment.BaseGeometry.Intersect(this._culvertLayoutLine.BaseGeometry).Item(0).PointOn2;
        return ret;
    }

    private DVector3d TryReverseVector(DVector3d baseVector, DVector3d vector)
    {
        return baseVector.DotProduct(vector) < 0 ? -vector : vector;
    }
}